//============================================================================
// Name        : GSP295_Wk4_HashTable
// Author      : Ronald Ram
// Version     :
// Copyright   : GSP295 Wk4 Program - Hash Tables
// Description : Hash Table Implemented as Array in C++
//============================================================================


#include "HashTable.h"


namespace GSP295_Wk4
{
	//C001: DEFAULT CONSTRUCTOR
	HashTable::HashTable()
	{
		this->length = hashSize;
		this->countFillCells = 0;
		Init();
	}

	//C002: OVERLOADED CONSTRUCTOR
	HashTable::HashTable( int size )
	{
		this->length = size;
		this->countFillCells = 0;
		Init();
	}	
	
	//C004: Init Method
	void HashTable::Init()
	{
		items = new string[length];
		for (int i=0; i<this->length; ++i) 
		{
			items[i] = "NULL";
		}
	}	

	//C003: DESTRUCTOR
	HashTable::~HashTable()
	{
		delete [] items;
		this->length = 0;
	}

	//Insert Method: method takes input string and uses the hash function to create 
	//an index, if the index is found in the array: 
	//the string is inserted into that element in the array, array size is incremented
	Result HashTable::Insert( string input )
	{
		Result result = HashFunction(input, INSERT);;

		//Index = -2: Overflow
		//Index = -1: Collision
		//Index = 0+: index found, OK to INSERT
		if ( result.resultType == SUCCESS ) //Index >= 0 
		{ 
			items[result.index] = input; 
			++this->countFillCells;
		}
		//Index == -1
		else if ( result.resultType == COLLISION ) 
		{ 
			items[result.index] = input; 
		} 
		//Index == -2, No action for FAILED
		return result; 
	}

	//Delete Method: method takes input string and uses the hash function to create 
	//an index, if the index is found in the array: 
	//the element is cleared and marked as 'DELETED', array size is decremented
	Result HashTable::Delete( string input )
	{
		Result result = HashFunction(input, DELETE);
		
		//Index = -2: No value found, OR Index out of bounds
		//Index = 0+: index found, OK to DELETE
		if ( result.resultType == SUCCESS ) 
		{ 
			items[result.index] = "DELETED";
			--this->countFillCells;
		}
		//Index == -2, No action for FAILED
		return result;
	}

	//Find Method: takes the input string and searches the array to find a match, using the hash function
	Result HashTable::Find( string input )
	{
		return HashFunction(input, FIND); 
	}

	//HASH Method: 
	//Pre: method takes in data input, and action to perform
	//Post: method returns status of action performed and an  index of where the action took place
	//Desptn: 
	//method runs a hash algorithm on input string and turns it into a index value.
	//It them compares the hashed index to determine if there is an index in the array that matches the conditions
	//in each Command requested
	Result HashTable::HashFunction( string input , ActionType action )
	{
		Result result;
		int hugeNum = 0;

		//////////////////////////////////////////////////////////////////////////
		for (int i=0; i< input.size(); ++i )
		{
			hugeNum = (hugeNum << 4) + input[i];
		}
		hugeNum %= hashSize;
		//////////////////////////////////////////////////////////////////////////

		result.index = hugeNum;

		//COMMAND: INSERT
		if ( action == INSERT )
		{
			//Index = -2: Overflow
			//Index = -1: Collision
			//Index = 0+: index found, OK to INSERT

			//Check upper/lower bounds
			if ( (result.index >= 0) && (result.index < this->length) )
			{
				//Check if insert into empty element
				if ( (this->items[result.index] == "NULL") || (this->items[result.index] == "DELETED") )
				{
					result.resultType = SUCCESS;
				}
				//Check for Collision
				else { result.resultType = COLLISION; }
			}
			//Index out of bounds
			else
			{
				result.resultType = FAILED;
				result.index = -2;
			}
		} 
		//COMMAND: DELETE
		else if ( action == DELETE )
		{
			//Index = -2: No value found, OR Index out of bounds
			//Index = 0+: index found, OK to DELETE

			//Check upper/lower bounds
			if ( (result.index >= 0) && (result.index < this->length) )
			{
				if ( this->items[result.index] == input )
				{
					result.resultType = SUCCESS;
				}
			}
			//No Match found
			else
			{
				result.resultType = FAILED;
				result.index = -2;
			}
		}
		//COMMAND: FIND
		else if ( action == FIND )
		{
			//Index = -2: No value found, OR Index out of bounds
			//Index = 0+: index found

			//Check upper/lower bounds
			if ( (result.index >= 0) && (result.index < this->length) )
			{
				if ( this->items[result.index] == input )
				{
					result.resultType = SUCCESS;
				}
			}
			//No Match found
			else
			{
				result.resultType = FAILED;
				result.index = -2;
			}
		}

		return result;
	}


	//Return T/F status if Array is Empty
	bool HashTable::isEmpty() const
	{
		return ( this->countFillCells == 0 );
	}


	//Reset Array, Initialise array
	void HashTable::Clear()
	{
		this->countFillCells = 0;
		
		for (int i=0; i< this->length; ++i) 
		{ 
			items[i] = "NULL"; 
		}

		if ( isEmpty() ) 
		{
			cout << "\n\n======================================\n" << endl;
			cout << "Successfully CLEARED - Array is now EMPTY...." << endl;
			cout << "\n======================================\n\n\n\n" << endl;
		} 
		else
		{
			cout << "\n\n======================================\n" << endl;
			cout << "Clear Command on array FAILED...." << endl;
			cout << "\n======================================\n\n\n\n" << endl;
		}
	}

	//Print all contents of array to screen
	void HashTable::Print()
	{
		cout << "\n\n===================================\n" << endl;
		cout << "RESULTS PRINTOUT:" << endl;
		cout << "------------------------------------\n" << endl;

		if ( !isEmpty() ) 
		{
			for (int i=0; i< this->length; ++i) 
			{
				cout << "Item [" << i << "] = " << items[i] << endl;
			}

			cout << "\n>>> " << this->countFillCells << " of " << this->length << "' elements filled in the Array <<<" << endl;
		} 
		else { cout << "Array is EMPTY...." << endl; }
		
		cout << "\n======================================\n\n\n\n" << endl;
	}
}//namespace